package com.app.tool;

import android.util.Base64;


import com.jiangdg.usbcamera.utils.Lf_Debug;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;


public class DESUtils
{
    @SuppressWarnings("unused")
	private static final String DES_ALGORITHM = "DES";
    @SuppressWarnings("unused")
	private static final String DES_TRANSFORMATION = "DES/ECB/NoPadding";
    @SuppressWarnings("unused")
	private static final String TDES_ALGORITHM = "DESede";
    @SuppressWarnings("unused")
	private static final String TDES_TRANSFORMATION = "DESede/ECB/NoPadding";

    public DESUtils() {}

    public static byte[] TDes(byte[] byteKey, byte[] data, boolean needPKCS5Padding)
    {
        if ((byteKey.length != 24) && (byteKey.length != 16)) {
            return null;
        }

        if (byteKey.length == 16) {
            byte[] key1 = BytesUtil.subByte(byteKey, 0, 8);
            byteKey = BytesUtil.merge(new byte[][] { byteKey, key1 });
        }

        byte[] encryptedData = des3(byteKey, needPKCS5Padding ? addPKCS5Padding(data) : data);
        return encryptedData;
    }

    public static byte[] UnTDes(byte[] byteKey, byte[] data, boolean hasPKCS5Padding)
    {
        if ((byteKey.length != 24) && (byteKey.length != 16)) {
            return null;
        }

        if (byteKey.length == 16) {
            byte[] key1 = BytesUtil.subByte(byteKey, 0, 8);
            byteKey = BytesUtil.merge(new byte[][] { byteKey, key1 });
        }

        byte[] decryptedData = undes3(byteKey, data);
        return hasPKCS5Padding ? delPKCS5Padding(decryptedData) : decryptedData;
    }

    public static byte[] Des(byte[] byteKey, byte[] data, boolean needPKCS5Padding)
    {
        if (byteKey.length != 8) {
            return null;
        }

        byte[] encryptedData = des(byteKey, needPKCS5Padding ? addPKCS5Padding(data) : data);
        return encryptedData;
    }

    public static byte[] UnDes(byte[] byteKey, byte[] data, boolean hasPKCS5Padding)
    {
        if (byteKey.length != 8) {
            return null;
        }

        byte[] decryptedData = undes(byteKey, data);
        
        return hasPKCS5Padding ? delPKCS5Padding(decryptedData) : decryptedData;
    }

    public static byte[] addPKCS5Padding(byte[] inData)
    {
        int paddingLen = 0;
        if (inData.length % 8 != 0) 
        {
            paddingLen = 8 - inData.length % 8;
        } 
        else 
        {
            paddingLen = 8;
        }
        byte[] padding = new byte[paddingLen];
        for (int i = 0; i < paddingLen; i++) 
        {
            padding[i] = ((byte)paddingLen);
        }
        return BytesUtil.merge(new byte[][] { inData, padding });
    }

    public static byte[] delPKCS5Padding(byte[] inData)
    {
        int paddingLen = 0;
        paddingLen = inData[(inData.length - 1)];
        if ((paddingLen == 0) || (paddingLen > 8)) {
            return inData;
        }
        byte[] outData = new byte[inData.length - paddingLen];
        System.arraycopy(inData, 0, outData, 0, inData.length - paddingLen);
        return outData;
    }

    private static byte[] des3(byte[] byteKey, byte[] data)
    {
        SecretKey deskey = new SecretKeySpec(byteKey, "DESede");
        try
        {
            Cipher c = Cipher.getInstance("DESede/ECB/NoPadding");
            c.init(1, deskey);
            return c.doFinal(data);
        } 
        catch (InvalidKeyException e) 
        {
            e.printStackTrace();
			Lf_Debug.e_printf(Lf_Debug._FILE_LINE_FUNC_(), e);
        } 
        catch (NoSuchAlgorithmException e) 
        {
            e.printStackTrace();
			Lf_Debug.e_printf(Lf_Debug._FILE_LINE_FUNC_(), e);
        } 
        catch (NoSuchPaddingException e) 
        {
            e.printStackTrace();
			Lf_Debug.e_printf(Lf_Debug._FILE_LINE_FUNC_(), e);
        } 
        catch (IllegalBlockSizeException e)
        {
            e.printStackTrace();
			Lf_Debug.e_printf(Lf_Debug._FILE_LINE_FUNC_(), e);
        } 
        catch (BadPaddingException e) 
        {
            e.printStackTrace();
			Lf_Debug.e_printf(Lf_Debug._FILE_LINE_FUNC_(), e);
        }
        return null;
    }

    private static byte[] undes3(byte[] byteKey, byte[] data)
    {
        SecretKey deskey = new SecretKeySpec(byteKey, "DESede");
        try
        {
            Cipher c = Cipher.getInstance("DESede/ECB/NoPadding");
            c.init(2, deskey);
            return c.doFinal(data);
        } 
        catch (InvalidKeyException e) 
        {
            e.printStackTrace();
			Lf_Debug.e_printf(Lf_Debug._FILE_LINE_FUNC_(), e);
        }
        catch (NoSuchAlgorithmException e) 
        {
            e.printStackTrace();
			Lf_Debug.e_printf(Lf_Debug._FILE_LINE_FUNC_(), e);
        } 
        catch (IllegalBlockSizeException e)
        {
            e.printStackTrace();
			Lf_Debug.e_printf(Lf_Debug._FILE_LINE_FUNC_(), e);
        }
        catch (BadPaddingException e)
        {
            e.printStackTrace();
			Lf_Debug.e_printf(Lf_Debug._FILE_LINE_FUNC_(), e);
        } 
        catch (NoSuchPaddingException e)
        {
            e.printStackTrace();
			Lf_Debug.e_printf(Lf_Debug._FILE_LINE_FUNC_(), e);
        }
        return null;
    }

    private static byte[] des(byte[] byteKey, byte[] data)
    {
        SecretKey deskey = new SecretKeySpec(byteKey, "DES");
        try
        {
            Cipher c = Cipher.getInstance("DES/ECB/NoPadding");
            c.init(1, deskey);
            return c.doFinal(data);
        } 
        catch (InvalidKeyException e) 
        {
            e.printStackTrace();
			Lf_Debug.e_printf(Lf_Debug._FILE_LINE_FUNC_(), e);
        } 
        catch (NoSuchAlgorithmException e) 
        {
            e.printStackTrace();
			Lf_Debug.e_printf(Lf_Debug._FILE_LINE_FUNC_(), e);
        } 
        catch (NoSuchPaddingException e) 
        {
            e.printStackTrace();
			Lf_Debug.e_printf(Lf_Debug._FILE_LINE_FUNC_(), e);
        } 
        catch (IllegalBlockSizeException e) 
        {
            e.printStackTrace();
			Lf_Debug.e_printf(Lf_Debug._FILE_LINE_FUNC_(), e);
        } 
        catch (BadPaddingException e) 
        {
            e.printStackTrace();
			Lf_Debug.e_printf(Lf_Debug._FILE_LINE_FUNC_(), e);
        }
        return null;
    }

    private static byte[] undes(byte[] byteKey, byte[] data)
    {
        SecretKey deskey = new SecretKeySpec(byteKey, "DES");
        try
        {
            Cipher c = Cipher.getInstance("DES/ECB/NoPadding");
            c.init(2, deskey);
            return c.doFinal(data);
        }
        catch (InvalidKeyException e) 
        {
            e.printStackTrace();
			Lf_Debug.e_printf(Lf_Debug._FILE_LINE_FUNC_(), e);
        } 
        catch (NoSuchAlgorithmException e) 
        {
            e.printStackTrace();
			Lf_Debug.e_printf(Lf_Debug._FILE_LINE_FUNC_(), e);
        } 
        catch (IllegalBlockSizeException e)
        {
            e.printStackTrace();
			Lf_Debug.e_printf(Lf_Debug._FILE_LINE_FUNC_(), e);
        } 
        catch (BadPaddingException e) 
        {
            e.printStackTrace();
			Lf_Debug.e_printf(Lf_Debug._FILE_LINE_FUNC_(), e);
        } 
        catch (NoSuchPaddingException e)
        {
            e.printStackTrace();
			Lf_Debug.e_printf(Lf_Debug._FILE_LINE_FUNC_(), e);
        }
        return null;
    }
    
    public static byte[] decryptBASE64(String key) throws Exception {
        return Base64.decode(key, Base64.DEFAULT);
    }

    public static String encryptBASE64(byte[] key) throws Exception {
        return Base64.encodeToString(key, Base64.DEFAULT);
    }

    public static byte[] encrypt3DesECB(byte[] data, byte[] key) {
        if(null==data || null==key || 0==data.length || 0==key.length){
            return null;
        }
        try {

            //恢复密钥
            SecretKey secretKey = new SecretKeySpec(format3DesKey(key), "DESede");
            //Cipher完成加密
            Cipher cipher = Cipher.getInstance(TRANSFORMATION_ECB);//"DESede");
            //cipher初始化
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            byte[] encrypt = cipher.doFinal(data);

            return encrypt;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static byte[] decrypt3DesECB(byte[] data, byte[] key) {
        if(null==data || null==key || 0==data.length || 0==key.length){
            return null;
        }

        try {

            //恢复密钥
            SecretKey secretKey = new SecretKeySpec(format3DesKey(key), "DESede");
            //Cipher完成加密
            Cipher cipher = Cipher.getInstance(TRANSFORMATION_ECB);//"DESede");
            //cipher初始化
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            byte[] encrypt = cipher.doFinal(data);

            return encrypt;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String initKey() throws Exception {
        return initKey(null);
    }

    public static String initKey(String seed) throws Exception {
        SecureRandom secureRandom = null;

        if (seed != null) {
            secureRandom = new SecureRandom(decryptBASE64(seed));
        } else {
            secureRandom = new SecureRandom();
        }

        KeyGenerator kg = KeyGenerator.getInstance("DES");
        kg.init(secureRandom);

        SecretKey secretKey = kg.generateKey();

        return encryptBASE64(secretKey.getEncoded());
    }

    private static byte[] format3DesKey(byte[] key){
        byte[] formatKey = null;
        if(key != null){
            int len = key.length;
            if(len > 0 && (0 == (len % 8))){
                formatKey = new byte[24];
                int srcLen = key.length;
                System.arraycopy(key, 0, formatKey, 0, 8);

                if(srcLen > 8){
                    System.arraycopy(key, 8, formatKey, 8, 8);
                }else {
                    System.arraycopy(key, 0, formatKey, 8, 8);
                }

                if(srcLen > 16){
                    System.arraycopy(key, 16, formatKey, 16, 8);
                }else {
                    System.arraycopy(key, 0, formatKey, 16, 8);
                }
            }
        }

        return formatKey;
    }

//    private static final String TRANSFORMATION = "DESede/ECB/PKCS5Padding";
//    private static final String TRANSFORMATION = "AES/ECB/PKCS5Padding";
//    private static final String TRANSFORMATION = "DESede/CBC/PKCS5Padding";
    private static final String TRANSFORMATION_CBC = "DESede/CBC/NoPadding";
    private static final String TRANSFORMATION_ECB = "DESede/ECB/NoPadding";

    private static byte[] ivs = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
    private static IvParameterSpec iv = new IvParameterSpec(ivs);

    public static byte[] decrypt3DesCBC(byte[] src, byte[] key) {
        try{
            //恢复密钥
            SecretKey secretKey = new SecretKeySpec(format3DesKey(key), "DESede");
            //Cipher完成解密
            Cipher cipher = Cipher.getInstance(TRANSFORMATION_CBC);//"DESede");
            //初始化cipher
            cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
            byte[] plain = cipher.doFinal(src);

            return plain;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    public static byte[] encrypt3DesCBC(byte[] src, byte[] key) {
        try {

            //恢复密钥
            SecretKey secretKey = new SecretKeySpec(format3DesKey(key), "DESede");
            //Cipher完成加密
            Cipher cipher = Cipher.getInstance(TRANSFORMATION_CBC);//"DESede");
            //cipher初始化
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
            byte[] encrypt = cipher.doFinal(src);

            return encrypt;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
